use std::any::{Any, TypeId};
use std::fmt::{Debug, Display, format};
use std::time::{Duration, Instant, SystemTime};

use anyhow::bail;
use chksum_md5::{Chksumable, default};
use log::{debug, error};
use reqwest::blocking::Client;
use reqwest::StatusCode;
use serde::Serialize;

use crate::{config, util};
use crate::common::CommError::NoneRequestData;
use crate::common::CommResult;
use crate::dto::{biz_code, task};
use crate::dto::base_dto::{ReqDto, ResDto};
use crate::dto::sys_dto::HostDto;
use crate::provider::host_provider::get_ip;

pub struct HClient {
    client: Client,
    context_path: String,
    port: String,
    host: String,
}

impl HClient {
    pub fn new(host: String, port: String, context_path: String) -> CommResult<HClient> {
        match Client::builder()
            .timeout(Duration::from_secs(30 * 60))
            .danger_accept_invalid_certs(true)
            .connect_timeout(Duration::from_secs(10))
            .build() {
            Ok(client) => {
                let hc = HClient {
                    client,
                    host,
                    port,
                    context_path,
                };
                Ok(hc)
            }
            Err(e) => {
                bail!("创建httpclient错误->{}",e)
            }
        }
    }

    pub(crate) fn post<T: ?Sized + Serialize + 'static + Debug>(&self, path: &str, code: u32, params: &T) -> CommResult<ResDto> {
        // let tsstr = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).map_or(String::new(), |d| d.as_secs().to_string());
        let tsstr = "".to_string();
        self.post_res(path, tsstr, code, params)
    }
    pub(crate) fn post_res<T: ?Sized + Serialize + 'static + Debug>(&self, path: &str, msg_id: String, code: u32, params: &T) -> CommResult<ResDto> {
        let mut req_dto = ReqDto {
            msg_id,
            host_id: config::get_hid(),
            host_ip: get_ip(),
            data: Default::default(),
            code,
            ts: SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap_or(Duration::from_secs(0)).as_secs(),
        };
        req_dto.data = data_json_str(params).unwrap_or_default();
        // 保持和ReqDto结构一致
        let data = util::to_json_string(&req_dto);
        if data == None {
            bail!(NoneRequestData)
        }
        let params = [("data", data.unwrap_or_default())];
        if code != biz_code::HEART_BEAT {
            if req_dto.data.len() > 4 * 256 {
                debug!("<<发送消息:code={code} data={:?}", &req_dto.data[0..256]);
            } else {
                debug!("<<发送消息:code={code} data={:?}", &req_dto.data);
            }
        }
        let res_builder = self.client.post(self.strip_url(path));
        match res_builder.form(&params).send() {
            Ok(res) => {
                let status = res.status();
                if status.is_success() {
                    match res.text() {
                        Ok(content) => {
                            debug!(">>接收到消息:{}",content);
                            let dtoop: Option<ResDto> = util::json_to_dto(&content);
                            if let Some(resdto) = dtoop {
                                if resdto.code == 0 {
                                    return Ok(resdto);
                                } else {
                                    bail!("服务器返回错误:{}",resdto.msg)
                                }
                            }
                            bail!("解析response数据错误:{}",content)
                        }
                        Err(e) => {
                            bail!("response text解析错误->{}",e);
                        }
                    }
                } else {
                    bail!("response ErrorCode->{}",res.status());
                }
            }
            Err(e) => {
                bail!("http response错误->{}",e);
            }
        }
    }
    fn strip_url(&self, path: &str) -> String {
        let mut s = String::from("http://");
        s.push_str(self.host.as_str());
        s.push_str(":");
        s.push_str(self.port.as_str());
        if !self.context_path.starts_with("/") {
            s.push_str("/");
        }
        if self.context_path.ne("/") {
            s.push_str(self.context_path.as_str());
        }
        s.push_str(path);
        s
    }
}

fn data_json_str<T: ?Sized + Debug + Serialize + 'static>(params: &T) -> Option<String> {
    if TypeId::of::<T>() == TypeId::of::<String>() {
        let ssfmt = format!("{:?}", params);
        if !ssfmt.is_empty() {
            //todo 为什么format后字符串会前后加个"(双引号)
            return Some(String::from(&ssfmt[1..ssfmt.len() - 1]));
        }
    } else {
        return util::to_json_string(params);
    }
    None
}